home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus Special 26
/
AMIGAplus Sonderheft 26 (2000)(Falke)(DE)(Track 1 of 2)[!].iso
/
PublicDomain
/
Anwendungen
/
BareED
/
source
/
AsyncStartup.c
next >
Wrap
C/C++ Source or Header
|
1999-11-21
|
20KB
|
708 lines
/*
Startup-code for BareED
Written 10 & 11/99 by Jörg van de Loo, Hövel 15, 47559 Kranenburg, FRG.
This startup-code is freeware and it's especially written in mind the
MaxonC++ compiler V4 (useful for C and ANSI-C modes only).
CLI parser 'borrowed' from AZTEC-C package (modified).
This startup codes sets up argc, argv with the commandline parameters
given through the CLI and with all selected files given through
Workbench, e.g. double click on project icon or shift select. Additional,
it ensures the present of a 68020 processor and Kickstart 2.0 (beta).
No stdin, stdout, stderr terminals will be set up since they are useless
for BareED.
SysBase and ExecBase are only present within this startup-code; this
ensures that the additional code of BareED doesn't refer to the variables
set up by the startup-code. This is due to the fact that my compiler
would otherwise address them as 32 bit addresses, that will cause each
time a reloc32 hunk entry. What I do is to refer to _DOS_Base and
_Exec_Base once, to set up DOSBase and SysBase in the additional code of
BareED, so that my compiler will them address from now on within BareED
indirect to the processor register a4.
When you compile this startup-code ensure that no 68020 or higher
instructions are used before the processor check is executed, otherwise
on a plain 68000 processor this startup-code will fail with a GURU.
This startup-code checks for the amount of free stack, too. Since this
startup-code uses very less stack (56 bytes when running and only up to
328 bytes while setting up variables) it should be enought when using a
stack size of only 4096 bytes for the loadfile of BareED, even under 3rd
party graphic emulation systems - which in fact do require much more
stack than the native Amiga OS 3 graphic system (up to 3Kb when BareED is
running).
One goal of this startup-code is, that it gains at lot of stack (up to
1.1 Kb) compared to the original startup-code that comes along with my
compiler.
Currently it needs a given stack size of 4052 bytes when started off a CLI
surround and 4046 bytes when started off the Workbench.
11/99 - modified this startup-code to run-back when started off a CLI
window or called with RunCommand() or within a batch script of an
application.
If you are able to adapt this startup-code to your compiler please do me
a favour and return the adapted startup-code for free to me, in this case
e-mail Marc Berson at:
BersonM@aol.com
and state clearly that the message is for me and concerned BareED.
NOTE: startup.c/BareED.c can be only compiled under GNU-C in large mode!
This is due to a bug in GNU-C; spilled registers.
I used the GNU-Compiler that can be found on Geek Gadgets II.
Thanks to Mr. Fish for this compilation.
*/
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#ifdef __GNUC__
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#else
#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#endif
#include <dos/dosextens.h>
#include <workbench/startup.h>
struct ExecBase *_Exec_Base;
struct DosLibrary *_DOS_Base;
struct Message *WBenchMsg;
#ifdef __MAXON__
#ifndef __cplusplus
static struct ExecBase *SysBase; // Allow only to appear within startup.c
static struct DosLibrary *DOSBase; // Allow only to appear within startup.c
#else
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
#endif
#endif
#ifndef __MAXON__
extern struct ExecBase *SysBase;
extern struct DOSLibrary *DOSBase;
#endif
static BPTR _prg_dir; // Remembered start dir
static unsigned int _argc, _arg_len;
static unsigned char **_argv, *_arg_lin;
#ifdef __MAXON__
extern "C" void GetBaseReg( void);
#define geta4() GetBaseReg()
#else
extern geta4();
#endif
struct LaunchStuff
{
int nextHunk;
int hunkSize;
char name[128];
int stack;
int pri;
int dummy; // To make this table divisible by 8
};
struct LaunchStuff *LS = 0;
struct Process *MasterTask;
unsigned char DOSName[] = "dos.library";
static unsigned char Console[] = "CON:0/0/320/80/ERROR REPORT/AUTO/CLOSE/WAIT";
static unsigned char NotEnoughtMem[]= "ERROR: Not enought memory!\n";
static unsigned char NotEnoughtStack[]= "ERROR: Stack < 4096 bytes!\n";
static unsigned char WrongCPU[] = "ERROR: CPU < 68020!\n";
static unsigned char WrongLIB[] = "ERROR: Library versions < 36!\n";
static unsigned char WrongSYS[] = "ERROR: System version < 36!\n";
static unsigned char WrongError[] = "ERROR: Non describt fault!\n"; // ????
void _main_jmp( unsigned long parlen, unsigned char *parameter);
int main( unsigned long argc, unsigned char **argv);
// ################################################################
#ifdef __GNUC__
void INIT_0_run_me_at_first_place( unsigned long parlen __asm("d0"), unsigned char *parameter __asm("a0") )
#else
void INIT_0_run_me_at_first_place( register __d0 unsigned long parlen, register __a0 unsigned char *parameter)
#endif
{
#ifndef __GNUC__ // Since GNU-C does not allow to compile BareED as base relative code
geta4();
#endif
_Exec_Base = SysBase = *((struct ExecBase **) 4); // From memory location 4 to exec library
if (_Exec_Base -> LibNode . lib_Version <= 32) // Kickstart lower than 33 (OS1.2) = die
return; // -> perhaps with a GURU (when started from WB)
_main_jmp( parlen, parameter); // We never return! We jump to _main_jmp because if
// we would do here the following stuff, d0 and a0
// would be ever pre-reserved, which blows up the
// objectfile un-necessary
}
// ################################################################
/* Following code is to avoid reloc32 entries - since they would otherwise
(when linked with the objectfiles) called via "jsr" and not via "bsr" */
static unsigned int strlenNR( register const unsigned char *str)
{
register unsigned int i = 0;
while (*str++)
i++;
return i;
}
static void strncpyNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
{
while (i)
{
*d++ = *s++;
i--;
}
*d = 0;
}
static void strcpyNR( register unsigned char *d, register const unsigned char *s)
{
while (*s)
*d++ = *s++;
*d = 0;
}
/* static void strcatNR( register unsigned char *d, register const unsigned char *s)
{
while (*d)
*d++;
while( *s)
*d++ = *s++;
*d = 0;
} */
static void strncatNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
{
while (*d)
*d++;
while( i)
{
*d++ = *s++;
i--;
}
*d = 0;
}
// ################################################################
/* Function required by GNU-C linker, not necessary anymore - since
all work already done by this startup-code! */
#ifdef __GNUC__
void __main( int argc, char **argv)
{
return;
}
#endif
// ################################################################
/* Taken without permission from the AZTEC-package, which in fact
can be found in books and magazins, too */
static void _cli_parse(struct Process *pp, unsigned long alen, register unsigned char *aptr)
{
register unsigned char *cp;
register struct CommandLineInterface *cli;
register unsigned char c;
cli = (struct CommandLineInterface *) ( (unsigned long) pp->pr_CLI << 2);
cp = (unsigned char *) ( (unsigned long) cli->cli_CommandName << 2);
_arg_len = (unsigned char) cp[0] + alen + 2;
if ( (_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), (0x10000) ) ) == 0)
return;
c = cp[0];
strncpyNR( _arg_lin, cp + 1, c);
_arg_lin[c] = ' ';
_arg_lin[c + 1] = 0;
strncatNR( _arg_lin, aptr, alen);
_arg_lin[c] = 0;
for (_argc = 1, aptr = cp = _arg_lin + c + 1; ; _argc++)
{
while ( (c = *cp) == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n')
cp++;
if (*cp < ' ')
break;
if (*cp == '"')
{
cp++;
while ( (c = *cp++) )
{
*aptr++ = c;
if (c == '"')
{
if (*cp == '"')
{
cp++;
}
else
{
aptr[-1] = 0;
break;
}
}
}
}
else
{
while ( (c = *cp++) && c != ' ' && c != '\t' && c != '\f' && c != '\r' && c != '\n')
*aptr++ = c;
*aptr++ = 0;
}
if (c == 0)
--cp;
}
*aptr = 0;
if ( (_argv = (unsigned char **) AllocMem( (((_argc + 1) * 4 + 7) & -8 ), (0x10000) ) ) == 0 )
{
_argc = 0;
return;
}
for (c=0, cp=_arg_lin; c < _argc; c++)
{
_argv[c] = cp;
cp += strlenNR( cp) + 1;
}
_argv[c] = 0;
}
// ################################################################
/* Allows several Workbench passed in arguments for BareED */
static void _wb_parse( struct WBStartup *msg)
{
int numargs, len, i;
char str[256], *curr; // Normally I'm against arrays on stack, but since we're
// at front of a loadfile it doesn't matter (because there
// is enough free stack available) and we can here avoid
// memory fragmentation through AllocMem()
numargs = msg -> sm_NumArgs;
_arg_len = (numargs + 2) * 4; // Number of arguments into amount bytes (plus 2 longwords)
/* Get length in bytes of all arguments including zero bytes and drawer terminators */
i = 0;
while (i < numargs)
{
if (msg -> sm_ArgList[i] . wa_Lock)
NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
_arg_len += strlenNR( &str[0]);
_arg_len += 2; // For zero byte and perhaps for drawer terminator "/" !
_arg_len += strlenNR( msg -> sm_ArgList[i] . wa_Name);
i++;
}
/* Allocate needed space for strings */
_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), (0x10000) );
if ( !_arg_lin)
return;
_argc = numargs;
_argv = (unsigned char **) _arg_lin;
curr = _arg_lin + ((numargs + 2) * 4);
/* Create and copy drawer and filenames into allocated memory, behind the argument pointers! */
i = 0;
while (i < numargs)
{
_argv[i] = curr;
if (msg -> sm_ArgList[i] . wa_Lock)
{
NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
strcpyNR( curr, &str[0]);
len = strlenNR( curr);
if ( curr[ len - 1] != ':')
{
curr[len] = '/';
len ++;
}
}
else
{
len = 0;
}
strcpyNR( curr + len, msg -> sm_ArgList[i] . wa_Name);
len = strlenNR( curr);
curr += len + 1; // Behind zero byte
i++; // Next arg
}
_prg_dir = CurrentDir( msg -> sm_ArgList[0] . wa_Lock); // Set up "progdir:"
}
// ################################################################
/* Print error code down to a console window, if there isn't one yet,
open one and give the message. */
static void GiveFault( int error)
{
BPTR newStdOut;
unsigned char *errorTxt;
if (_DOS_Base) // DOSBase set up?
{
errorTxt = 0;
if (_Exec_Base -> LibNode . lib_Version <= 35) // OS 2.0
Console[27] = 0; // No, OS 1.x, so remove AUTO/CLOSE/WAIT from console description
if (WBenchMsg) // If WB-start, open console window
{
newStdOut = Open( Console, MODE_OLDFILE);
}
else
{
newStdOut = Output();
}
if (newStdOut)
{
if (error == 236)
errorTxt = WrongSYS;
if (error == 105)
errorTxt = WrongCPU;
if (error == 122)
errorTxt = WrongLIB;
if (error == 217)
errorTxt = NotEnoughtStack;
if (error == 103)
errorTxt = NotEnoughtMem;
if (errorTxt == 0)
errorTxt = WrongError; // ????
Write( newStdOut, errorTxt, strlenNR( errorTxt) );
if (WBenchMsg && _Exec_Base -> LibNode . lib_Version <= 35) // If WB-start and OS 1.x
{
Delay( 5*60); // Wait a while
Close( newStdOut); // Close console
}
}
}
}
// ################################################################
/* Un-load startup-code and additional program code/data and bss.
Free also the table we used, but restore the segment of our
program first!
This is only called if we are in 'RunBack' mode. */
static void LaunchExit( void)
{
unsigned int *adr;
adr = (unsigned int *) &(INIT_0_run_me_at_first_place);
adr --;
*adr = LS -> nextHunk; // Restore the link to the additional hunks
adr --;
*adr = LS -> hunkSize; // Restore first hunk's size (was set zero)
FreeMem( LS, sizeof( struct LaunchStuff));
Forbid(); // Must be called because we may not removed from memory since additional code must be run first!
adr += 1; // Point back to segment entry for DOS
UnLoadSeg( (BPTR) ((unsigned long) adr/4) );
}
// ################################################################
/* Code to get program's return address and to call it
That's the 'additional code that must be run first' */
static const unsigned short _finally_code[] =
{
0x2400, // move.l D0,D2 Save error code
0x93C9, // suba.l A1,A1
0x2C78,0x0004, // movea.l (4).w,A6
0x4EAE,0xFEDA, // jsr _LVOFindTask(A6) Own Task
0x2040, // movea.l D0,A0
0x2068,0x00B0, // movea.l pr_ReturnAddr(A0),A0 Get system's exit address for us
0x4FE8,0xFFFC, // lea -4(A0),sp Restore initial stack
0x2002, // move.l D2,D0 Error code to D0
0x4E75 // rts Back to system
};
// ################################################################
/* Since my compiler doesn't allow to overload a function (stub), I use
a different name: exitNR ! */
void exitNR( int error)
{
#ifdef __GNUC__
void (* _finally)( int err __asm("d0") );
#else
void (* _finally)( register __d0 int err);
#endif
geta4();
(void *) _finally = (void *) _finally_code;
if (error)
GiveFault( error);
((struct Process *) _Exec_Base ->ThisTask) -> pr_Result2 = error;
if (WBenchMsg)
{
if (_arg_lin)
{
FreeMem( _arg_lin, ((_arg_len + 7) & -8) );
if (_prg_dir) // If it is zero (SYS:) ignore
CurrentDir( _prg_dir); // Lock of basic directory
}
Forbid();
ReplyMsg( WBenchMsg);
}
else
{
if (_arg_lin)
{
FreeMem(_argv, (((_argc + 1) * 4 + 7) & -8) );
FreeMem(_arg_lin, ((_arg_len + 7) & -8) );
}
}
if (_DOS_Base)
CloseLibrary( (struct Library *) _DOS_Base);
// Check if our application did a detach from a CLI
if (LS)
LaunchExit();
_finally( error);
}
// ################################################################
/* This function does not return to the caller ! */
static void CallMainPRG( void)
{
exitNR( main( _argc, _argv));
}
// ################################################################
/* If we are in 'RunBack' mode we call the main() function. The code
below is executed as first part of the new created process. */
static void LaunchMain( void)
{
geta4();
Signal( (struct Task *) MasterTask, (0x10000));
CallMainPRG();
}
// ################################################################
/* Set up necessary thing to provide an auto-detach (RunBack mode).
This is a little tricky one which cares about the priority and
stack size of the original process which are the settings for
the new created.
*/
static void LaunchStartup( void)
{
struct MsgPort *msgp;
struct Process *proc;
unsigned int *adr;
struct TagItem tag[5];
struct CommandLineInterface *cli;
unsigned char *cp;
MasterTask = ((struct Process *) _Exec_Base -> ThisTask);
LS = (struct LaunchStuff *) AllocMem( sizeof( struct LaunchStuff), MEMF_CLEAR|MEMF_PUBLIC);
if (!LS) // No table?
CallMainPRG(); // We never return!
cli = (struct CommandLineInterface *) ( (unsigned long) MasterTask->pr_CLI << 2);
cp = (unsigned char *) ( (unsigned long) cli->cli_CommandName << 2);
// Duplicate name: slave task's name equal to master's one
strncpyNR( LS->name, cp + 1, cp[0]);
// Duplicate priority
LS->pri = MasterTask -> pr_Task . tc_Node . ln_Pri;
// Duplicate stack size
LS->stack = (int) MasterTask->pr_Task.tc_SPUpper - (int) MasterTask->pr_Task.tc_SPLower;
// Settings for task to create
tag[0].ti_Tag = NP_Entry;
tag[0].ti_Data = (ULONG) &(LaunchMain);
tag[1].ti_Tag = NP_Name;
tag[1].ti_Data = (ULONG) LS->name;
tag[2].ti_Tag = NP_StackSize;
tag[2].ti_Data = LS->stack;
tag[3].ti_Tag = NP_Priority;
tag[3].ti_Data = LS->pri;
tag[4].ti_Tag = TAG_DONE;
/* An AmigaDOS 'Hunk' looks like this (in memory)
-4 int HunkSize
0 BPTR NextHunk <- The segment starts here (as BPTR), see LoadSeg(), UnLoadSeg(), etc.
+4 Code/Data/BSS
*/
// Create slave task which becomes master task
msgp = (struct MsgPort *) CreateNewProc( tag);
if (!msgp) // Failed to create task?
{
FreeMem( LS, sizeof( struct LaunchStuff));
LS = 0;
CallMainPRG(); // We never return!
}
// From message port of process (task) to process itself (slave)
proc = (struct Process *) ((ULONG) msgp - sizeof( struct Task));
Wait( (0x10000)); // Wait for slave task to signal this bit so that we can quit savely
// First instruction of this startup-code is at location:
adr = (unsigned int *) &(INIT_0_run_me_at_first_place);
// Point to BPTR next hunk (seglist start)
adr --;
// Remember the hunk address
LS -> nextHunk = *adr;
// Break the hunk list of this loadfile
*adr = 0;
// Point to size of 'this' hunk (first hunk of loadfile)
adr --;
// Remember hunk size (normally code size, it's the HUNK_CODE)
LS -> hunkSize = *adr;
// This hunk size forced to zero
*adr = 0;
/* Why I did the things above?
Remember ever that 2 tasks are running which share the same memory and instructions!
If we would now quit the first by the system (user) created task, the memory and
therewith the instructions are gone, too. Thus a crash isn't far. What I do is to
tell AmigaDOS that the first created process has got only a single hunk with a size
of zero (ok., I know I'm a liar) so that AmigaDOS doesn't frees anything when the
first created task quits. The segment (hunk-list) is restored by function
LaunchExit() and the memory used by it is removed by a call to AmigaDOS function
UnLoadSeg(). Since UnLoadSeg() instantly gives the memory back to the system I
forbid task switching and therwith memory allocation while the 2nd created task is
staying alive. If it dies, the task switching is enabled automatically through the
system.
*/
}
// ################################################################
void _main_jmp( unsigned long parlen, unsigned char *parameter)
{
int error;
struct Process *proc;
error = 0;
proc = ((struct Process *) _Exec_Base -> ThisTask);
if (proc -> pr_CLI)
{
WBenchMsg = 0;
}
else
{
WaitPort( &proc -> pr_MsgPort);
WBenchMsg = GetMsg( &proc -> pr_MsgPort);
}
if (_Exec_Base -> LibNode . lib_Version <= 35) // Lower Kickstart 2.0 (beta)?
error = 236;
_DOS_Base = DOSBase = (struct DosLibrary *) OpenLibrary( DOSName, 33);
if ( !_DOS_Base || _DOS_Base -> dl_lib . lib_Version <= 35)
error = 122;
// If we have a soft-kicked 2.0 ROM for 1.x machines we raise at least an Exec error (236)
if (error)
exitNR( error);
if ( !(_Exec_Base -> AttnFlags & AFF_68020)) // At least a 68020 processor?
exitNR( 105);
// Do we have at least 4000 bytes of stack available? - NOTE: the variable proc is on
// the top of the stack so using this address is the current upper bound of the stack!
if ( (((int) proc->pr_Task.tc_SPUpper - (int) proc->pr_Task.tc_SPLower) - ( (int) proc->pr_Task.tc_SPUpper - (int) &proc) ) < 4000 )
exitNR( 217);
if (WBenchMsg)
{
_wb_parse( (struct WBStartup *) WBenchMsg);
if ( !_arg_lin)
exitNR( 103); // Fail: no mem
}
else
{
_cli_parse( proc, parlen, parameter);
if ( !_arg_lin)
exitNR( 103);
}
if (WBenchMsg)
{
CallMainPRG();
}
else
{
LaunchStartup(); // Try detach from CLI, if it fails, go on normal
}
}
// ################################################################